%%% Copyright 2010 Google Inc.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%%     http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.

%%% @copyright 2010 Google Inc. All Rights Reserved.
%%% @author <jacoby@google.com> Jacoby Thwaites [http://sites/madcloud]
%%% @version {@version}
%%% @doc
%%% Utility module that encapsulates the functions that generate structs
%%% from txm objects.
%%%
%%% The `struct' format is:
%%% <ul>
%%% <li>`{atom()}' - a simple atom</li>
%%% <li>`{atom(), list(attribute()}' - an atom with a set of key/value
%%% tuples</li>
%%% <li>`{atom(), list(attribute()), list(struct())}' - an atom with both
%%% attributes and a list of (recursive) `struct' content.</li>
%%% </ul>
%%% @headerfile "../include/txm.hrl"
%%% @headerfile "../include/web_srv.hrl"

-module(struct).

-include("txm.hrl").
-include("struct.hrl").

-export([contents_to_structs/2, record_to_struct/1]).
-export([nr_set_to_structs/1]).

%% @doc Converts a {nr_set(), list()} to a list of struct where the
%% nr_set becomes the index.
%% @spec contents_to_structs(ref(), {nr_set(), list()}) -> struct()
-spec(contents_to_structs(ref(), {nr_set(), list()}) -> struct()).
contents_to_structs({?FOLDER, Id}, {NrSet, Records}) ->
  IndexStruct = {?PROP_INDEX, [{?PROP_ID, Id}], nr_set_to_structs(NrSet)},
  RecordStructs = [record_to_struct(Record) || Record <- Records],
  [IndexStruct | RecordStructs].

%% @doc Converts a record to a struct suitable for XML or JSON output.
%% @spec record_to_struct(rec()) -> struct()
-spec(record_to_struct(rec()) -> struct()).
record_to_struct(#?FOLDER{id = Id, entries = Entries}) ->
  {?PROP_FOLDER, [{?PROP_ID, Id}], nr_set_to_structs(Entries)};

%% #2
record_to_struct(#?OP{isComposite = true} = Op) ->
  {?PROP_OP,
   % Attributes
   [{?PROP_ID, Op#?OP.id},
    {?PROP_OP_TYPE, Op#?OP.opType},
    {?PROP_IS_COMPOSITE, Op#?OP.isComposite},
    {?PROP_ROOT_FOLDER_REF, Op#?OP.rootFolderRef}],

   % Content list
   [ mask_to_struct(Op#?OP.mask),
     input_to_struct(Op#?OP.input),
     outputs_to_struct(Op#?OP.outputs),
     versions_to_struct(Op#?OP.versions)]};

%% #3
record_to_struct(#?OP{isComposite = false} = Op) ->
  {?PROP_OP,
   % Attributes
   [{?PROP_ID, Op#?OP.id},
    {?PROP_OP_TYPE, Op#?OP.opType},
    {?PROP_IS_COMPOSITE, Op#?OP.isComposite}],

   % Content list
   [ mask_to_struct(Op#?OP.mask),
     input_to_struct(Op#?OP.input),
     outputs_to_struct(Op#?OP.outputs)]};

%% #4
record_to_struct(#?FIELD{id = Id, type = Type}) ->
  {?PROP_FIELD, [{?PROP_ID, Id},
                 {?PROP_TYPE, Type}]}.

%% @doc Converts a mask dict to a struct suitable for XML or JSON output.
%% @spec mask_to_struct(mask()) -> struct()
mask_to_struct(NrSet) ->
  {?PROP_MASK_SET, [], nr_set_to_structs(NrSet)}.

%% @doc Converts an input dict to a struct suitable for XML or JSON output.
%% @spec input_to_struct(input()) -> struct()
input_to_struct(NrSet) ->
  {?PROP_INPUT_SET, [], nr_set_to_structs(NrSet)}.

%% @doc Converts an outputs nr_set_dict into a struct suitable for XML or JSON
%% output.
%% @spec outputs_to_struct(nr_set_dict()) -> struct()
outputs_to_struct(NrSetDict) ->
  {?PROP_OUTPUT_SETS, [],
    nr_set_dict:fold(
     fun(OutputKey, NrSet, Acc) ->
          [{?PROP_OUTPUT_SET, [{?PROP_NAME, OutputKey}],
            nr_set_to_structs(NrSet)} | Acc]
     end,
     [], NrSetDict)}.

%% @doc Converts an nr_set to a list of struct suitable
%% for XML or JSON output.
%% @spec nr_set_to_structs(nr_set()) -> list(term())
nr_set_to_structs(NrSet) ->
  nr_set:fold(
    fun(Name, {Type, RefId}, Acc) when is_list(Name) ->
         [{Type, [{?PROP_NAME, Name}, {?PROP_ID, RefId}]} | Acc]
    end,
    [], NrSet).

%% @doc Converts a list of version tuples to a struct suitable for XML or JSON
%% output.
%% @spec versions_to_struct(list()) -> struct()
versions_to_struct(Versions) ->
  {?PROP_VERSIONS, [],
   % A version is {integer(), string(), ref()}. We need a list
   % of struct, one per version element.
   lists:foldl(fun({VsnNum, VsnLabel, FolderRef}, Acc) ->
                    [{?PROP_VERSION,
                      [{?PROP_VSN_NUM, VsnNum},
                       {?PROP_VSN_LABEL, VsnLabel},
                       {?PROP_VSN_REF, FolderRef}]} | Acc]
               end,
               [], Versions)}.
